package cc.iotkit.jql;

import cc.iotkit.jql.executor.*;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.StrUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.jdbc.core.JdbcTemplate;
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;

import java.lang.reflect.*;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
@Data
public class JqlBeanFactory<T> implements FactoryBean<T>, BeanFactoryAware, InvocationHandler {

    private Class<T> interfaceClass;

    private JdbcTemplate jdbcTemplate;

    private Map<Class<?>, ScriptSqlReader> sqlReaderMap = new HashMap<>();

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) {
        Type elementType = method.getReturnType();
        //根据不同返回数据类型使用不同执行器
        IExecutor executor;
        if (elementType == List.class) {
            //取List的泛型类型作为返回的元素类型
            Type[] types = ((ParameterizedTypeImpl) method.getGenericReturnType()).getActualTypeArguments();
            elementType = types[0];
            if (elementType == ObjData.class) {
                executor = new MapListExecutor();
            } else {
                executor = new ListExecutor<>();
            }
        } else if (ClassUtil.isBasicType((Class<?>) elementType)) {
            executor = new ValueExecutor<>();
        } else if (elementType == ObjData.class) {
            executor = new MapExecutor();
        } else {
            executor = new BeanExecutor<>();
        }

        //取方法参数名与参数值
        Map<String, Object> params = new HashMap<>();
        Object[] names = Arrays.stream(method.getParameters()).map(Parameter::getName).toArray();
        for (int i = 0; i < names.length; i++) {
            params.put(names[i].toString(), args[i]);
        }

        //对非原生对象解构作为参数
        for (Object arg : args) {
            if (arg != null && !ClassUtil.isBasicType(arg.getClass())) {
                params.putAll(BeanUtil.beanToMap(arg));
            }
        }

        //取sql读取器
        Class<?> interfaceCls = proxy.getClass().getInterfaces()[0];
        if (!sqlReaderMap.containsKey(interfaceCls)) {
            try {
                sqlReaderMap.putIfAbsent(interfaceCls, new ScriptSqlReader(interfaceCls));
            } catch (Throwable e) {
                throw new RuntimeException("get ScriptSqlReader error", e);
            }
        }

        String sql;
        ScriptSqlReader sqlReader = sqlReaderMap.get(interfaceCls);
        try {
            //获取调用方法的sql
            sql = sqlReader.getSql(method, args, params);
        } catch (Throwable e) {
            throw new RuntimeException("get script sql error", e);
        }
        if (StrUtil.isBlank(sql)) {
            throw new RuntimeException("get script sql is blank");
        }

        //执行sql查询
        return executor.executor(jdbcTemplate, sql, (Class) elementType, params);
    }

    @Override
    public T getObject() {
        final Class[] interfaces = {interfaceClass};
        return (T) Proxy.newProxyInstance(this.getClass().getClassLoader(), interfaces, this);
    }

    @Override
    public Class<?> getObjectType() {
        return interfaceClass;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.jdbcTemplate = beanFactory.getBean(JdbcTemplate.class);
    }
}
